home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / SciAn / src / ScianTimers.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  60KB  |  2,652 lines

  1. /*ScianTimers.c
  2.   Timers in scian
  3.   Eric Pepke
  4.   September 9, 1990
  5. */
  6.  
  7. #include "Scian.h"
  8. #include "ScianTypes.h"
  9. #include "ScianArrays.h"
  10. #include "ScianLists.h"
  11. #include "ScianErrors.h"
  12. #include "ScianWindows.h"
  13. #include "ScianObjWindows.h"
  14. #include "ScianIDs.h"
  15. #include "ScianTimers.h"
  16. #include "ScianScripts.h"
  17. #include "ScianDialogs.h"
  18. #include "ScianSliders.h"
  19. #include "ScianSpaces.h"
  20. #include "ScianDraw.h"
  21. #include "ScianStyle.h"
  22. #include "ScianColors.h"
  23. #include "ScianTextBoxes.h"
  24. #include "ScianMethods.h"
  25. #include "ScianDepend.h"
  26. #include "ScianPictures.h"
  27. #include "ScianEvents.h"
  28. #include "ScianControls.h"
  29. #include "ScianDatasets.h"
  30. #include "ScianFontSystem.h"
  31.  
  32. extern ObjPtr curSpace;            /*The current space*/
  33. ObjPtr timedObjClass;            /*Time dependant object*/
  34. ObjPtr timeControlClass;        /*Class for time controls*/
  35. double clockStarted = 0.0;        /*Time at which clock was started*/
  36. double clockStopped = 0.0;        /*Time clock was stopped*/
  37. double videoClock = 0.0;        /*Video clock*/
  38. Bool clockOn = true;            /*True iff clock is on*/
  39. long startTime;                /*Starting time of machine*/
  40.  
  41. #define SCROLLTIME            /*Autoscroll the time control*/
  42.  
  43. static real dummy = 1.0;
  44.  
  45. #define CEDGE    3
  46.  
  47. AlarmRec alarms[NALARMS];        /*The alarms*/
  48.  
  49. /*Internal prototypes*/
  50. #ifdef PROTO
  51. static int TimeToPixel(ObjPtr control, real time);
  52. static real PixelToTime(ObjPtr, int);
  53. #else
  54. static int TimeToPixel();
  55. static real PixelToTime();
  56. #endif
  57.  
  58. /*States for time reader machine*/
  59. #define TS_BEFORESIGN    1        /*Before plus or minus sign*/
  60. #define TS_INWHOLE    2        /*Whole part*/
  61. #define TS_INFRAC    3        /*Fractional part*/
  62. #define TS_BEFOREEXP    4        /*Before pluse or minus exponent*/
  63. #define TS_INEXP    6        /*In exponent*/
  64. #define TS_DONE        7
  65.  
  66. void TinyDelay()
  67. /*Does a tiny little delay*/
  68. {
  69.     int k;
  70.     for (k = 0; k < 10; ++k)
  71.     dummy = dummy * dummy;
  72. }
  73.  
  74. #ifdef PROTO
  75. int ParseTime(real *t, int *f, char s[])
  76. #else
  77. int ParseTime(t, f, s)
  78. real *t;
  79. int *f;
  80. char s[];
  81. #endif
  82. /*Parses a time in s.  Puts the time format in f and the time in t.
  83.   Returns >0 if it worked.  Returns <=0 if there was an error*/
  84. {
  85.     double time;
  86.     int format;
  87.     double curNumber;
  88.     double sign = 1.0;
  89.     double fracDiv;
  90.     double expSign;
  91.     double curExp = 0.0;
  92.     int k;
  93.     int state = TS_BEFORESIGN;
  94.  
  95.     format = 0;
  96.  
  97.     time = 0.0;
  98.     curNumber = 0.0;
  99.  
  100.     for (k = 0; s[k] && (state != TS_DONE); ++k)
  101.     {
  102.     if (!isspace(s[k]))
  103.     {
  104.         switch(state)
  105.         {
  106.         case TS_BEFORESIGN:    /*Before number sign*/
  107.             if (s[k] == '-')
  108.             {
  109.             sign = -1.0;
  110.             break;
  111.             }
  112.             else if (s[k] == '+')
  113.             {
  114.             state = TS_INWHOLE;
  115.             break;
  116.             }
  117.             else
  118.             {
  119.             state = TS_INWHOLE;
  120.             /*Fall through*/
  121.             }
  122.         case TS_INWHOLE:        /*In the whole part of the number*/
  123.             format |= TF_SECONDS;
  124.             if (s[k] == ':')
  125.             {
  126.             /*Next is minutes or seconds.  Save and continue*/
  127.             format |= format << 1;
  128.             time += curNumber;
  129.             time *= 60.0;
  130.             curNumber = 0.0;
  131.             }
  132.             else if (s[k] >= '0' && s[k] <= '9')
  133.             {
  134.             curNumber *= 10.0;
  135.             curNumber += s[k] - '0';
  136.             }
  137.             else if (s[k] == '.')
  138.             {
  139.             fracDiv = 1.0;
  140.             state = TS_INFRAC;
  141.             format |= TF_SUBSECONDS;
  142.             }
  143.             else if (s[k] == 'E' || s[k] == 'e' ||
  144.                  s[k] == 'F' || s[k] == 'f' ||
  145.                  s[k] == 'G' || s[k] == 'g')
  146.             {
  147.             expSign = 1.0;
  148.             curExp = 0.0;
  149.             state = TS_BEFOREEXP;
  150.             }
  151.             else
  152.             {
  153.             /*Error*/
  154.             return -k;
  155.             }
  156.             break;
  157.         case TS_INFRAC:            /*In the fractional part*/
  158.             format |= TF_SUBSECONDS;
  159.             if (s[k] >= '0' && s[k] <= '9')
  160.             {
  161.             fracDiv *= 0.1;
  162.             curNumber += fracDiv * (double) (s[k] - '0');
  163.             }
  164.             else if (s[k] == 'E' || s[k] == 'e' ||
  165.                  s[k] == 'F' || s[k] == 'f' ||
  166.                  s[k] == 'G' || s[k] == 'g')
  167.             {
  168.             expSign = 1.0;
  169.             curExp = 0.0;
  170.             state = TS_BEFOREEXP;
  171.             }
  172.             else
  173.             {
  174.             /*Error*/
  175.             return -k;
  176.             }
  177.             break;
  178.         case TS_BEFOREEXP:
  179.             format |= TF_SUBSECONDS;
  180.             if (s[k] == '-')
  181.             {
  182.             expSign = -1.0;
  183.             format = TS_INEXP;
  184.             break;
  185.             }
  186.             else if (s[k] == '+')
  187.             {
  188.             format = TS_INEXP;
  189.             break;
  190.             }
  191.             /*Fall through*/
  192.         case TS_INEXP:
  193.             if (s[k] >= '0' && s[k] <= '9')
  194.             {
  195.             curExp *= 10.0;
  196.             curExp += (double) (s[k] - '0');
  197.             }
  198.             else return -k;
  199.         }
  200.     }
  201.     }
  202.  
  203.     /*Finish up filling in the time*/
  204.     if (curExp != 0.0)
  205.     {
  206.     curNumber *= pow(10.0, curExp * expSign);
  207.     curExp = 0.0;
  208.     }
  209.     time += curNumber * sign;
  210.     *t = time;
  211.     *f = format;
  212.  
  213.     return k;
  214. }
  215.  
  216. ObjPtr NewTimedObject(timeSteps, timeData)
  217. ObjPtr timeSteps, timeData;
  218. /*Returns a new timed object made up of timeSteps and timeData pointing to 
  219.   repObj*/
  220. {
  221.     ObjPtr timedObj, stepsArray, dataArray;
  222.     real *stepElements;
  223.     ObjPtr *dataElements;
  224.     ThingListPtr stepsRunner, dataRunner;
  225.     long nSteps, k;
  226.  
  227.     nSteps = ListCount(timeSteps);
  228.     stepsArray = NewArray(AT_REAL, 1, &nSteps);
  229.     dataArray = NewArray(AT_OBJECT, 1, &nSteps);
  230.     if (!stepsArray || !dataArray)
  231.     {
  232.     return NULLOBJ;
  233.     }
  234.  
  235.     stepsRunner = LISTOF(timeSteps);
  236.     dataRunner = LISTOF(timeData);
  237.     stepElements = ELEMENTS(stepsArray);
  238.     dataElements = ELEMENTS(dataArray);
  239.     k = 0;
  240.     while (stepsRunner)
  241.     {
  242.     if (!dataRunner)
  243.     {
  244.         ReportError("NewTimedObject", "Not enough data for timesteps");
  245.     }
  246.     else
  247.     {
  248.         stepElements[k] = GetReal(stepsRunner -> thing);
  249.         dataElements[k] = dataRunner -> thing;
  250.     }
  251.     stepsRunner = stepsRunner -> next;
  252.     dataRunner = dataRunner -> next;
  253.     ++k;
  254.     }
  255.  
  256.     timedObj = NewObject(timedObjClass, 0);
  257.     SetVar(timedObj, TIMESTEPS, stepsArray);
  258.     SetVar(timedObj, TIMEDATA, dataArray);
  259.     return timedObj;
  260. }
  261.  
  262. ObjPtr MakeTimeBounds(timedObj)
  263. ObjPtr timedObj;
  264. /*Calculates the time bounds of a timedObj*/
  265. {
  266.     ObjPtr timeSteps;        /*Array of time steps*/
  267.     real *timeElements;
  268.     real tb[2];
  269.     ObjPtr timeBounds;
  270.  
  271.     MakeVar(timedObj, TIMESTEPS);
  272.     timeSteps = GetVar(timedObj, TIMESTEPS);
  273.     
  274.     if (!timeSteps)
  275.     {
  276.     return ObjFalse;
  277.     }
  278.  
  279.     timeElements = ELEMENTS(timeSteps);
  280.  
  281.     /*New method of doing time step*/
  282.     if (DIMS(timeSteps)[0] == 1)
  283.     {
  284.     tb[0] = tb[1] = timeElements[0];
  285.     }
  286.     else
  287.     {
  288.     tb[0] = timeElements[0] +
  289.         0.5 * (timeElements[0] - timeElements[1]);
  290.     tb[1] = timeElements[DIMS(timeSteps)[0] - 1] +
  291.         0.5 * ( timeElements[DIMS(timeSteps)[0] - 1] -
  292.             timeElements[DIMS(timeSteps)[0] - 2]);
  293.     }
  294.  
  295.     timeBounds = NewRealArray(1, 2L);
  296.     CArray2Array(timeBounds, tb);
  297.     SetVar(timedObj, TIMEBOUNDS, timeBounds);
  298.     return ObjTrue;
  299. }
  300.  
  301. Bool InsertTimeSlice(timedObj, time, data)
  302. ObjPtr timedObj, time, data;
  303. /*Inserts a time slice of data at time into timedObj*/
  304. {
  305.     ObjPtr timeSteps, timeData;
  306.     long index;
  307.     real *elements;
  308.     real curTime;
  309.  
  310.     MakeVar(timedObj, TIMESTEPS);
  311.     timeSteps = GetArrayVar("InsertTimeSlice", timedObj, TIMESTEPS);
  312.     timeData = GetVar(timedObj, TIMEDATA);
  313.     
  314.     if (!timeSteps || !timeData)
  315.     {
  316.     return false;
  317.     }
  318.  
  319.     curTime = GetReal(time);
  320.     index = SearchReal(timeSteps, curTime);
  321.     if (index < 0)
  322.     {
  323.     return false;
  324.     }
  325.  
  326.     /*See if this is a duplication of an existing time step*/
  327.     elements = ELEMENTS(timeSteps);
  328.     if (index < DIMS(timeSteps)[0] && elements[index] == curTime)
  329.     {
  330.     /*DIKEO change to replace the time step later*/
  331.     return true;
  332.     }
  333.     if (index - 1 >= 0 && elements[index - 1] == curTime)
  334.     {
  335.     return true;
  336.     }
  337.  
  338.     timeSteps = InsertInArray(timeSteps, time, index);
  339.     timeData = InsertInArray(timeData, data, index);
  340.  
  341.     SetVar(timedObj, TIMESTEPS, timeSteps);
  342.     SetVar(timedObj, TIMEDATA, timeData);
  343.  
  344.     if (index == 0 || index >= DIMS(timeSteps)[0])
  345.     {
  346.     MakeVar(timedObj, TIMEBOUNDS);
  347.     }
  348.  
  349.     return true;
  350. }
  351.  
  352. double WallClock()
  353. /*Returns the wall clock time*/
  354. {
  355.     struct tms buffer;
  356.  
  357.     return ((double) (times(&buffer) - startTime)) / HEARTBEAT - clockStarted;
  358. }
  359.  
  360. double Clock()
  361. /*Returns the clock.  This differs between real-time and single-frame.*/
  362. {
  363.     if (runningScript)
  364.     {
  365.     return videoClock - clockStarted;
  366.     }
  367.     else
  368.     {
  369.     return WallClock();
  370.     }
  371. }
  372.  
  373. void SetSystemClock(time)
  374. double time;
  375. /*Sets the system clock to time*/
  376. {
  377.     struct tms buffer;
  378.     double supposedTime;
  379.  
  380.     if (runningScript)
  381.     {
  382.     supposedTime = videoClock;
  383.     }
  384.     else
  385.     {
  386.     supposedTime = ((double) (times(&buffer) - startTime)) / HEARTBEAT;
  387.     }
  388.     clockStarted = time - supposedTime;
  389. }
  390.  
  391. void ClockOff()
  392. /*Turns the clock off for a moment*/
  393. {
  394.     struct tms buffer;
  395.  
  396.     if (clockOn)
  397.     {
  398.     clockOn = false;
  399.     if (runningScript)
  400.     {
  401.         clockStopped = videoClock;
  402.     }
  403.     else
  404.     {
  405.         clockStopped = ((double) (times(&buffer) - startTime)) / HEARTBEAT;
  406.     }
  407.     }
  408. }
  409.  
  410. void ClockOn()
  411. /*Turns the clock back on*/
  412. {
  413.     struct tms buffer;
  414.  
  415.     if (!clockOn)
  416.     {
  417.     clockOn = true;
  418.     if (runningScript)
  419.     {
  420.         clockStarted += videoClock - clockStopped;
  421.     }
  422.     else
  423.     {
  424.         clockStarted += ((double) (times(&buffer) - startTime)) / HEARTBEAT - clockStopped;
  425.     }
  426.     }
  427. }
  428.  
  429. ObjPtr RegisterTimedField(timedObj, whichField)
  430. ObjPtr timedObj;
  431. int whichField;
  432. /*Registers a timed object*/
  433. {
  434.     FuncTyp method;
  435.     ObjPtr time;
  436.     ObjPtr floor, ceiling;
  437.     Bool interpolateP;
  438.     ObjPtr timeSteps, timeData;        /*Time steps and data*/
  439.     long index;
  440.     real *elements;
  441.     real diffFloor, diffCeiling;
  442.  
  443.     /*See if we need to interpolate*/
  444.     MakeVar(timedObj, INTERPOLATEP);
  445.     interpolateP = GetPredicate(timedObj, INTERPOLATEP);
  446.  
  447.     /*Get the time steps*/
  448.     MakeVar(timedObj, TIMESTEPS);
  449.     timeSteps = GetArrayVar("FindFloorTimestep", timedObj, TIMESTEPS);
  450.  
  451.     /*Get the timedata*/
  452.     MakeVar(timedObj, TIMEDATA);
  453.     timeData = GetVar(timedObj, TIMEDATA);
  454.     
  455.     if (!timeSteps || !timeData)
  456.     {
  457.     ReportError("RegisterTimedField", "No timesteps or data");
  458.     return ObjFalse;
  459.     }
  460.  
  461.     /*Find the closest upper bound time step*/
  462.     index = SearchReal(timeSteps, spaceTime);
  463.  
  464.     if (index < 0)
  465.     {
  466.     ceiling = NULLOBJ;
  467.     }
  468.     else if (index >= DIMS(timeSteps)[0])
  469.     {
  470.     ceiling = NULLOBJ;
  471.     }
  472.     else
  473.     {
  474.     ceiling = GetObjectElement(timeData, &index);
  475.     }
  476.  
  477.     --index;
  478.  
  479.     if (index < 0)
  480.     {
  481.     floor = NULLOBJ;
  482.     }
  483.     else if (index >= DIMS(timeSteps)[0])
  484.     {
  485.     floor = NULLOBJ;
  486.     }
  487.     else
  488.     {
  489.     floor = GetObjectElement(timeData, &index);
  490.     }
  491.  
  492.     if (!floor && !ceiling)
  493.     {
  494.     /*Neither floor nor ceiling, error and return*/
  495.     ReportError("RegisterTimedField", "No floor or ceiling");
  496.     return ObjFalse;
  497.     }
  498.  
  499.     if (!floor)
  500.     {
  501.     /*No floor, must use ceiling*/
  502.  
  503.     method = GetMethodSurely("RegisterTimedField", ceiling, REGISTERFIELD);
  504.     if (method)
  505.     {
  506.         ObjPtr result;
  507.         result = (*method)(ceiling, whichField);
  508.         return result;
  509.     }
  510.     }
  511.     else if (!ceiling)
  512.     {
  513.     /*No ceiling, must use floor*/
  514.  
  515.     method = GetMethodSurely("RegisterTimedField", floor, REGISTERFIELD);
  516.     if (method)
  517.     {
  518.         ObjPtr result;
  519.         result = (*method)(floor, whichField);
  520.         return result;
  521.     }
  522.     }
  523.     else
  524.     {
  525.     elements = ELEMENTS(timeSteps);
  526.  
  527.     if (interpolateP)
  528.     {
  529.         /*Set up to interpolate later by setting the two timesteps*/
  530.  
  531.         diffFloor = ABS(elements[index] - spaceTime);
  532.         diffCeiling = ABS(elements[index + 1] - spaceTime);
  533.  
  534.         method = GetMethodSurely("RegisterTimedField", floor, REGISTERFIELD);
  535.         if (method)
  536.         {
  537.         ObjPtr result;
  538.         result = (*method)(floor, whichField);
  539.         }
  540.         curFields[whichField] . weight = diffCeiling / (diffFloor + diffCeiling);
  541.         curFields[whichField] . groupInterp = true;
  542.  
  543.         method = GetMethodSurely("RegisterTimedField", ceiling, REGISTERFIELD);
  544.         if (method)
  545.         {
  546.         ObjPtr result;
  547.         result = (*method)(ceiling, whichField + MAXNCURFIELDS);
  548.         }
  549.         curFields[whichField + MAXNCURFIELDS] . weight = diffFloor / (diffFloor + diffCeiling);
  550.         curFields[whichField + MAXNCURFIELDS] . groupInterp = true;
  551.         return ObjTrue;
  552.     }
  553.     else
  554.     {
  555.         /*No interpolation, just use closest time step*/
  556.         ObjPtr closest;
  557.  
  558.         diffFloor = ABS(elements[index] - spaceTime);
  559.         diffCeiling = ABS(elements[index + 1] - spaceTime);
  560.  
  561.         if (diffFloor <= diffCeiling)
  562.         {
  563.         closest = floor;
  564.         }
  565.         else
  566.         {
  567.         closest = ceiling;
  568.         }
  569.         method = GetMethodSurely("RegisterTimedField", closest, REGISTERFIELD);
  570.         if (method)
  571.         {
  572.         ObjPtr result;
  573.         result = (*method)(closest, whichField);
  574.         return result;
  575.         }
  576.     }
  577.     }
  578.     return NULLOBJ;
  579. }
  580.  
  581. #if 0
  582. ObjPtr MakeTimedCurData(timedObj)
  583. ObjPtr timedObj;
  584. /*Makes a timed object's timeData*/
  585. {
  586.     ObjPtr timeSteps, timeData;        /*Time steps and data*/
  587.     ObjPtr retVal;            /*Value to return*/
  588.     ObjPtr timeBounds;            /*Time bounds of the current space*/
  589.     ObjPtr time;            /*Current spaceTime*/
  590.     ObjPtr curObjTime;            /*Current time for the object*/
  591.     ObjPtr interp1, interp2;        /*Two arrays to interpolate between*/
  592.     real time1, time2;            /*Times to interpolate*/
  593.     long index;
  594.     ObjPtr element;            /*Elements of the array*/
  595.     Bool interpolateP;
  596.  
  597.     MakeVar(timedObj, TIMEBOUNDS);
  598.     timeBounds = GetVar(timedObj, TIMEBOUNDS);
  599.  
  600.     MakeVar(timedObj, INTERPOLATEP);
  601.     interpolateP = GetPredicate(timedObj, INTERPOLATEP);
  602.  
  603.     if ((curObjTime = GetVar(timedObj, LASTTIME)) &&
  604.     (GetReal(curObjTime) == spaceTime) &&
  605.     (interpolateP == GetPredicate(timedObj, LASTINTERP)))
  606.     {
  607.     retVal = GetVar(timedObj, CURDATA);
  608.     SetVar(timedObj, CURDATA, retVal);
  609.     return ObjTrue;
  610.     }
  611.  
  612.     SetVar(timedObj, LASTINTERP, interpolateP ? ObjTrue : ObjFalse);
  613.     MakeVar(timedObj, TIMESTEPS);
  614.     timeSteps = GetArrayVar("MakeTimedCurData", timedObj, TIMESTEPS);
  615.     timeData = GetVar(timedObj, TIMEDATA);
  616.     
  617.     if (!timeSteps || !timeData)
  618.     {
  619.     ReportError("MakeTimedCurData", "No timesteps or data");
  620.     return ObjFalse;
  621.     }
  622.  
  623.     /*Find the closest lower bound time step*/
  624.     index = SearchReal(timeSteps, spaceTime);
  625.  
  626.     if (index <= 0)
  627.     {
  628.     retVal = GetObjectElement(timeData, &index);
  629.     }
  630.     else if (index >= DIMS(timeSteps)[0])
  631.     {
  632.     --index;
  633.     retVal = GetObjectElement(timeData, &index);
  634.     ++index;
  635.     }
  636.     else
  637.     {
  638.     /*There's something to choose from or interpolate*/
  639.     real weight;
  640.     
  641.     --index;
  642.     interp1 = GetObjectElement(timeData, &index);
  643.     ++index;
  644.     interp2 = GetObjectElement(timeData, &index); 
  645.     time1 = ((real *) ELEMENTS(timeSteps))[index - 1];
  646.     time2 = ((real *) ELEMENTS(timeSteps))[index];
  647.     weight = (spaceTime - time1) / (time2 - time1);
  648.     if (interpolateP && IsRealArray(((ObjPtr *) ELEMENTS(timeData))[index - 1]))
  649.     {
  650.         retVal = InterpArray(interp1, interp2, weight);
  651.     }
  652.     else if (weight > 0.5)
  653.     {
  654.         retVal = ((ObjPtr *) ELEMENTS(timeData))[index];
  655.     }
  656.     else
  657.     {
  658.         retVal = ((ObjPtr *) ELEMENTS(timeData))[index - 1];
  659.     }    
  660.     }
  661.     SetVar(timedObj, CURDATA, retVal);
  662.     SetVar(timedObj, LASTTIME, NewReal(spaceTime));
  663.     return retVal;
  664. }
  665. #endif
  666.  
  667. Bool WakeMe(object, method, time)
  668. ObjPtr object;
  669. NameTyp method;
  670. double time;
  671. /*Puts in a request to wake an object by sending it a method at a certain
  672.   time.  Returns true iff succeeds.  The method will be passed a double
  673.   giving the lateness of the wakeup call*/
  674. {
  675.     int k;
  676.     for (k = 0; k < NALARMS; ++k)
  677.     {
  678.     if (alarms[k] . object == NULLOBJ)
  679.     {
  680.         alarms[k] . object = object;
  681.         alarms[k] . method = method;
  682.         alarms[k] . when = time;
  683.         alarms[k] . wallTime = false;
  684.         alarms[k] . startTime = Clock();
  685.         AddToReferenceList(object);
  686.         return true;
  687.     }
  688.     }
  689.     return false;
  690. }
  691.  
  692. Bool SetTimeout(object, method, time)
  693. ObjPtr object;
  694. NameTyp method;
  695. double time;
  696. /*Puts in a request to wake an object by sending it a method at a certain
  697.   time.  Returns true iff succeeds.  The method will be passed a double
  698.   giving the lateness of the wakeup call*/
  699. {
  700.     int k;
  701.     for (k = 0; k < NALARMS; ++k)
  702.     {
  703.     if (alarms[k] . object == NULLOBJ)
  704.     {
  705.         alarms[k] . object = object;
  706.         alarms[k] . method = method;
  707.         alarms[k] . when = time;
  708.         alarms[k] . wallTime = true;
  709.         alarms[k] . startTime = WallClock();
  710.         AddToReferenceList(object);
  711.         return true;
  712.     }
  713.     }
  714.     return false;
  715. }
  716.  
  717. void DoNotDisturb(object, method)
  718. ObjPtr object;
  719. NameTyp method;
  720. /*Removes all wakeup calls for object and method*/
  721. {
  722.     int k;
  723.     for (k = 0; k < NALARMS; ++k)
  724.     {
  725.     if (alarms[k] . object == object &&
  726.         alarms[k] . method == method)
  727.     {
  728.         ObjPtr object;
  729.         object = alarms[k] . object;
  730.         alarms[k] . object = 0;
  731.         DeleteThing(object);
  732.     }
  733.     }
  734. }
  735.  
  736. void IdleTimers()
  737. /*Idles all the timer stuff*/
  738. {
  739.     int k;
  740.     for (k = 0; k < NALARMS; ++k)
  741.     {
  742.     double time;
  743.     
  744.     if (alarms[k] . wallTime)
  745.     {
  746.         time = WallClock();
  747.     }
  748.     else
  749.     {
  750.         time = Clock();
  751.     }
  752.  
  753.     if (alarms[k] . object && time >= alarms[k] . when)
  754.     {
  755.         ObjPtr object;
  756.         FuncTyp method;
  757.  
  758.         object = alarms[k] . object;
  759.         alarms[k] . object = 0;
  760.         method = GetMethod(object, alarms[k] . method);
  761.         if (method)
  762.         {
  763.         (*method)(object, time - alarms[k] . startTime);
  764.         }
  765.         DeleteThing(object);
  766.     }
  767.     }
  768. }
  769.  
  770. #ifdef PROTO
  771. static int TimeToPixel(ObjPtr control, real time)
  772. #else
  773. static int TimeToPixel(control, time)
  774. ObjPtr control;
  775. real time;
  776. #endif
  777. /*Returns the x pixel for a specific time in a time control.
  778.   Check this against bounds of thing before drawing*/
  779. {
  780.     ObjPtr var, scrollbar;
  781.     real value, tpp;
  782.     int l, r, b, t, mid;
  783.  
  784.     Get2DIntBounds(control, &l, &r, &b, &t);
  785.     
  786.     l += TCDSWIDTH + 2 * TCGAP + BARWIDTH;
  787.     mid = (l + r) / 2;
  788.  
  789.     scrollbar = GetVar(control, HSCROLL);
  790.     if (scrollbar)
  791.     {
  792.     var = GetValue(scrollbar);
  793.     if (var)
  794.     {
  795.         value = GetReal(var);
  796.     }
  797.     else
  798.     {
  799.         value = 0.0;
  800.     }
  801.     }
  802.     else
  803.     {
  804.     value = 0.0;
  805.     }
  806.  
  807.     var = GetVar(control, TIMEPERPIXEL);
  808.     if (var)
  809.     {
  810.     tpp = GetReal(var);
  811.     }
  812.     else
  813.     {
  814.     return mid;
  815.     }
  816.     return (time - value) / tpp + 0.5 + mid;
  817. }
  818.  
  819. static real PixelToTime(control, pixel)
  820. ObjPtr control;
  821. int pixel;
  822. /*Returns the time for a specific x pixel in a time control.
  823.   Check this against bounds of thing before drawing*/
  824. {
  825.     ObjPtr var, scrollbar;
  826.     real value, tpp;
  827.     int l, r, b, t, mid;
  828.  
  829.     Get2DIntBounds(control, &l, &r, &b, &t);
  830.     
  831.     l += TCDSWIDTH + 2 * TCGAP + BARWIDTH;
  832.     mid = (l + r) / 2;
  833.  
  834.     scrollbar = GetVar(control, HSCROLL);
  835.     if (scrollbar)
  836.     {
  837.     var = GetValue(scrollbar);
  838.     if (var)
  839.     {
  840.         value = GetReal(var);
  841.     }
  842.     else
  843.     {
  844.         value = 0.0;
  845.     }
  846.     }
  847.     else
  848.     {
  849.     value = 0.0;
  850.     }
  851.  
  852.     var = GetVar(control, TIMEPERPIXEL);
  853.     if (var)
  854.     {
  855.     tpp = GetReal(var);
  856.     }
  857.     else
  858.     {
  859.     return value;
  860.     }
  861.     
  862.     return (pixel - mid) * tpp + value;
  863. }
  864.  
  865. #ifdef PROTO
  866. void PrintTime(char *s, real t, int f)
  867. #else
  868. void PrintTime(s, t, f)
  869. char *s;
  870. real t;
  871. int f;
  872. #endif
  873. /*Prints time t using predefined format f into string s*/
  874. {
  875.     long hours;
  876.     long minutes;
  877.     long seconds;
  878.  
  879.     if (f & TF_HOURS)
  880.     {
  881.     hours = t / 3600;
  882.     sprintf(s, "%d:", hours);
  883.     while (*s) ++s;
  884.  
  885.     minutes = t / 60;
  886.      sprintf(s, "%02d", minutes - hours * 60);
  887.     while (*s) ++s;
  888.     
  889.     if (f & TF_SECONDS)
  890.     {
  891.         seconds = t;
  892.         if (f & TF_SUBSECONDS)
  893.         {
  894.         sprintf(s, ":%02g", t - minutes * 60);
  895.         }
  896.         else
  897.         {
  898.         sprintf(s, ":%02d", seconds - minutes * 60);
  899.         }
  900.     }
  901.     }
  902.     else if (f & TF_MINUTES)
  903.     {
  904.     minutes = t / 60;
  905.     sprintf(s, "%d:", minutes);
  906.     while (*s) ++s;
  907.  
  908.     seconds = t;
  909.     if (f & TF_SUBSECONDS)
  910.     {
  911.         sprintf(s, "%02g", t - minutes * 60);
  912.     }
  913.     else
  914.     {
  915.         sprintf(s, "%02d", seconds - minutes * 60);
  916.     }
  917.     }
  918.     else
  919.     {
  920.     sprintf(s, "%g", t);
  921.     }
  922. }
  923.  
  924. void UpdateTimeReadout(control)
  925. ObjPtr control;
  926. /*Updates a time readout in a control*/
  927. {
  928.     ObjPtr readout, clock, format, var;
  929.     char timeStr[256];
  930.     FuncTyp method;
  931.  
  932.     readout = GetObjectVar("UpdateTimeReadout", control, READOUT);
  933.     if (!readout)
  934.     {
  935.     return;
  936.     }
  937.  
  938.     clock = GetObjectVar("UpdateTimeReadout", control, REPOBJ);
  939.     if (!clock)
  940.     {
  941.     return;
  942.     }
  943.  
  944.     var = GetVar(control, VALUE);
  945.  
  946.     if (var)
  947.     {
  948.     MakeVar(control, TIMEFORMAT);
  949.     format = GetVar(control, TIMEFORMAT);
  950.     if (format)
  951.     {
  952.         PrintTime(timeStr, GetReal(var), GetInt(format));
  953.     }
  954.     else
  955.     {
  956.         sprintf(timeStr, "%g", GetReal(var));
  957.     }
  958.     }
  959.     else
  960.     {
  961.     strcpy(timeStr, "");
  962.     }
  963.     InhibitLogging(true);
  964.     method = GetMethod(readout, CHANGEDVALUE);
  965.     SetMethod(readout, CHANGEDVALUE, (FuncTyp) 0);
  966.     SetTextBox(readout, timeStr);
  967.     SetMethod(readout, CHANGEDVALUE, method);
  968.     InhibitLogging(false);
  969. }
  970.  
  971. ObjPtr DrawTimeControl(object)
  972. ObjPtr object;
  973. /*Draws a time control*/
  974. {
  975. #ifdef GRAPHICS
  976.     ObjPtr hScroll, vScroll, readout, var, timeVar;
  977.     int left, right, bottom, top, timePix;
  978.     real value;
  979.     real downOffset;
  980.     ObjPtr datasets;
  981.     Bool madeDatasets;
  982.     ObjPtr clock;
  983.     long line;
  984.  
  985.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  986.  
  987.     SetUIFont(TCFONT);
  988.  
  989.     if (MakeVar(object, DATASETS))
  990.     {
  991.     RecalcScroll(object);
  992.     }
  993.  
  994.     MakeVar(object, TIMESTEPS);
  995.  
  996.     /*Get the clock*/
  997.     clock = GetObjectVar("DrawTimeControl", object, REPOBJ);
  998.     if (!clock)
  999.     {
  1000.     return NULLOBJ;
  1001.     }
  1002.  
  1003.     /*Get current time*/
  1004.     timeVar = GetVar(object, VALUE);
  1005.     if (timeVar)
  1006.     {
  1007.     value = GetReal(timeVar);
  1008.     }
  1009.     else
  1010.     {
  1011.     value = 0.0;
  1012.     }
  1013.  
  1014.     /*Draw the scroll bars*/
  1015.     hScroll = GetVar(object, HSCROLL);
  1016.     if (hScroll)
  1017.     {
  1018.     DrawObject(hScroll);
  1019.     }
  1020.  
  1021.     vScroll = GetVar(object, VSCROLL);
  1022.     if (vScroll)
  1023.     {
  1024.     DrawObject(vScroll);
  1025.     }
  1026.  
  1027.     /*Draw the readout*/
  1028.     readout = GetVar(object, READOUT);
  1029.     if (readout)
  1030.     {
  1031.     DrawObject(readout);
  1032.     }
  1033.  
  1034.     datasets = GetVar(object, DATASETS);
  1035.  
  1036.     /*Draw the datasets box*/
  1037.     FillUIRect(left + 1, left + TCDSWIDTH - 1, bottom + TCGAP + BARWIDTH + 1, top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1, UIGRAY62);
  1038.  
  1039.     /*Draw the contents of the datasets box*/
  1040.     if (vScroll && datasets)
  1041.     {
  1042.     downOffset = GetReal(GetValue(vScroll));
  1043.     line = (-downOffset / TCCELLHEIGHT);
  1044.     if (line < DIMS(datasets)[0])
  1045.     {
  1046.         /*It's worth printing*/
  1047.         int texty;
  1048.         ObjPtr element;
  1049.         SetClipRect(left + 1,
  1050.             left + TCDSWIDTH - 1,
  1051.             bottom + TCGAP + BARWIDTH + 1,
  1052.             top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1);
  1053.         texty = top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1 - (line + 1) * TCCELLHEIGHT + TCTEXTBOFF - downOffset;
  1054.         SetUIColor(UIBLACK);
  1055.         while (line < DIMS(datasets)[0] &&
  1056.            texty > bottom + TCGAP + BARWIDTH + 1 - TCCELLHEIGHT)
  1057.         {
  1058.         ObjPtr name;
  1059.         element = GetObjectElement(datasets, &line);
  1060.         name = GetVar(element,  NAME);
  1061.         if (name)
  1062.         {
  1063.             DrawAString(LEFTALIGN, left + 1 + TCTEXTLOFF,
  1064.                    texty,
  1065.                    GetString(name)); 
  1066.         }
  1067.         else
  1068.         {
  1069.             DrawAString(LEFTALIGN, left + 1 + TCTEXTLOFF,
  1070.                    texty,
  1071.                    "?");
  1072.         }
  1073.         texty -= TCCELLHEIGHT;
  1074.         ++line;
  1075.         }
  1076.         RestoreClipRect();
  1077.     }
  1078.     }
  1079.     /*Draw the frame of the datasets box*/
  1080.     FrameUIRect(left, left + TCDSWIDTH, bottom + TCGAP + BARWIDTH, top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP, UIBLACK);
  1081.  
  1082.     /*Draw the timeline box*/
  1083.     FillUIRect(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1084.         right - 1,
  1085.         bottom + TCGAP + BARWIDTH + 1,
  1086.         top - 1,
  1087.         UIGRAY62);
  1088.  
  1089.     /*Draw the contents of the timeline box*/
  1090.     if (vScroll && datasets)
  1091.     {
  1092.     ObjPtr var;
  1093.     char timeStr[256];
  1094.     int format;
  1095.  
  1096.     MakeVar(object, TIMEFORMAT);
  1097.     var = GetVar(object, TIMEFORMAT);
  1098.     if (var)
  1099.     {
  1100.         format = GetInt(var);
  1101.     }
  1102.     else
  1103.     {
  1104.         format = TF_SECONDS + TF_SUBSECONDS;
  1105.     }
  1106.  
  1107.     /*Draw the time numbers*/
  1108.     var = GetVar(object, TIMEPERPIXEL);
  1109.     if (var)
  1110.     {
  1111.         /*It's valid; draw a whole range of times*/
  1112.         real timePerPixel, timeAtPixel;
  1113.         real displayStep;
  1114.         int displayTics;
  1115.         long tempLong;
  1116.         int curPixel;
  1117.         int timePixel;
  1118.         real curTime;
  1119.         int k;
  1120.         timePerPixel = GetReal(var);
  1121.  
  1122.         /*Get a pixel that's way off the left*/
  1123.         timePixel = left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1 - TCMAXTIMEWIDTH;
  1124.         timeAtPixel = PixelToTime(object, timePixel);
  1125.  
  1126.         /*Get the step and tics*/
  1127.         var = GetRealVar("DrawTimeControl", object, DISPLAYSTEP);
  1128.         if (var)
  1129.         {
  1130.         displayStep = GetReal(var);
  1131.         }
  1132.         else
  1133.         {
  1134.         displayStep = 1.0;
  1135.         }
  1136.         var = GetIntVar("DrawTimeControl", object, DISPLAYTICS);
  1137.         if (var)
  1138.         {
  1139.         displayTics = GetInt(var);
  1140.         }
  1141.         else
  1142.         {
  1143.         displayTics = 10;
  1144.         }
  1145.  
  1146.         /*Do the truncate bit*/
  1147.         tempLong = timeAtPixel / displayStep;
  1148.         timeAtPixel = tempLong * displayStep;
  1149.         timePixel = TimeToPixel(object, timeAtPixel);
  1150.  
  1151.         /*Write out the times*/
  1152.         SetClipRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1153.             right - 1,
  1154.             top - TCTIMEHEIGHT + 1,
  1155.             top - 1);
  1156.         curPixel = timePixel;
  1157.         curTime = timeAtPixel;
  1158.         SetUIFont(TCTIMEFONT);
  1159.         SetUIColor(UIBLACK);
  1160.         do
  1161.         {
  1162.         if (format)
  1163.         {
  1164.             PrintTime(timeStr, curTime, format);
  1165.         }
  1166.         else
  1167.         {
  1168.             sprintf(timeStr, "%g", curTime);
  1169.         }
  1170.         DrawAString(CENTERALIGN, curPixel, 
  1171.                top - TCTIMEHEIGHT + 1 + TCTIMEBOFF,
  1172.                timeStr);
  1173.         curTime += displayStep;
  1174.         tempLong = curTime / displayStep + 0.5;
  1175.         curTime = tempLong * displayStep;
  1176.         curPixel = TimeToPixel(object, curTime);
  1177.         }
  1178.         while (curPixel < right + TCMAXTIMEWIDTH);
  1179.  
  1180.         /*Write out upper tic marks*/
  1181.         curPixel = timePixel;
  1182.         curTime = timeAtPixel;
  1183.         k = 0;
  1184.         SetUIColor(UIBLACK);
  1185.         do
  1186.         {
  1187.         DrawUILine(curPixel, top - TCTIMEHEIGHT + (k % displayTics ? TCTIMEBOFF / 2 : TCTIMEBOFF) - 1,
  1188.                curPixel, top - TCTIMEHEIGHT + 1,
  1189.                UIGRAY25);
  1190.         ++k;
  1191.         curTime += displayStep / displayTics;
  1192.         curPixel = TimeToPixel(object, curTime);
  1193.         }
  1194.         while (curPixel < right + TCMAXTIMEWIDTH);
  1195.         RestoreClipRect();
  1196.  
  1197.         /*Write out lower tic marks*/
  1198.         SetClipRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1199.             right - 1,
  1200.             bottom + BARWIDTH + TCGAP + 1,
  1201.             top - TCTIMEHEIGHT - TCCURHEIGHT - TCGAP - 1);
  1202.         curPixel = timePixel;
  1203.         curTime = timeAtPixel;
  1204.         do
  1205.         {
  1206.         DrawUILine(curPixel, top - TCTIMEHEIGHT - TCCURHEIGHT - TCGAP - 1,
  1207.                curPixel, bottom + BARWIDTH + TCGAP + 1,
  1208.                UIGRAY25);
  1209.         curTime += displayStep;
  1210.         curPixel = TimeToPixel(object, curTime);
  1211.         }
  1212.         while (curPixel < right + TCMAXTIMEWIDTH);
  1213.         RestoreClipRect();
  1214.     }
  1215.     else if (timeVar)
  1216.     {
  1217.         /*Just draw a single time at the current time slider*/
  1218.         real curTime;
  1219.         int timePixel;
  1220.     
  1221.         curTime = value;
  1222.  
  1223.         timePixel = TimeToPixel(object, curTime);
  1224.  
  1225.         /*Write out the time*/
  1226.         SetClipRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1227.             right - 1,
  1228.             top - TCTIMEHEIGHT + 1,
  1229.             top - 1);
  1230.         SetUIFont(TCTIMEFONT);
  1231.         SetUIColor(UIBLACK);
  1232.         if (format)
  1233.         {
  1234.         PrintTime(timeStr, curTime, format);
  1235.         }
  1236.         else
  1237.         {
  1238.         sprintf(timeStr, "%g", curTime);
  1239.         }
  1240.         DrawAString(CENTERALIGN, timePixel, 
  1241.                top - TCTIMEHEIGHT + 1 + TCTIMEBOFF,
  1242.                timeStr);
  1243.         RestoreClipRect();
  1244.     }
  1245.  
  1246.     /*Draw the time lines*/
  1247.     downOffset = GetReal(GetValue(vScroll));
  1248.     line = (-downOffset / TCCELLHEIGHT);
  1249.     if (line < DIMS(datasets)[0])
  1250.     {
  1251.         /*It's worth printing*/
  1252.         int midy;
  1253.         ObjPtr element;
  1254.         SetClipRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1255.             right - 1,
  1256.             bottom + TCGAP + BARWIDTH + 1,
  1257.             top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1);
  1258.         midy = top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1 - (line + 1) * TCCELLHEIGHT + TCLINEBOFF - downOffset;
  1259.         SetUIColor(UIBLACK);
  1260.         while (line < DIMS(datasets)[0] &&
  1261.            midy > bottom + TCGAP + BARWIDTH + 1 - TCCELLHEIGHT)
  1262.         {
  1263.         /*Print out the timeline of the object*/
  1264.         Bool interpolateP;
  1265.  
  1266.         ObjPtr timeSteps;
  1267.         element = GetObjectElement(datasets, &line);
  1268.         MakeVar(element, TIMESTEPS);
  1269.         timeSteps = GetVar(element, TIMESTEPS);
  1270.         MakeVar(element, INTERPOLATEP);
  1271.         interpolateP = GetPredicate(element, INTERPOLATEP);
  1272.  
  1273.         if (timeSteps)
  1274.         {
  1275.             /*Object with time steps.  Find the first*/
  1276.             int pixel;
  1277.             long ts1, ts2;
  1278.             real leftTime, rightTime;
  1279.  
  1280.             /*Get a pixel that's clear off the left side*/
  1281.             pixel = left;
  1282.             leftTime = PixelToTime(object, pixel);
  1283.  
  1284.             /*Get its index*/
  1285.             ts1 = SearchReal(timeSteps, leftTime);
  1286.             if (ts1 > 0)
  1287.             {
  1288.             --ts1;
  1289.             }
  1290.             {
  1291.             /*There's a chance it can be drawn.  Get a pixel 
  1292.               clear off the right*/
  1293.             pixel = right + TCSAMPLESIZE;
  1294.             rightTime = PixelToTime(object, pixel);
  1295.  
  1296.             /*Get its index*/
  1297.             ts2 = SearchReal(timeSteps, rightTime);
  1298.             if (ts2 >= DIMS(timeSteps)[0])
  1299.             {
  1300.                 --ts2;
  1301.             }
  1302.             if (ts2 >= ts1)
  1303.             {
  1304.                 /*DrawIt*/
  1305.                 real *timeElements;
  1306.                 long k;
  1307.                 long coords[2];
  1308.                 int p1, p2;
  1309.  
  1310.                 timeElements = (real *) ELEMENTS(timeSteps);
  1311.                 element = GetObjectElement(datasets, &line);
  1312.  
  1313.                 p1 = TimeToPixel(object, timeElements[ts1]);
  1314.                 for (k = ts1 + 1; k <= ts2; ++k)
  1315.                 {
  1316.                 p2 = TimeToPixel(object, timeElements[k]);
  1317.                 if (!interpolateP)
  1318.                 {
  1319.                     DrawUILine((p1 + p2) / 2, midy + TCMIDTICHEIGHT,
  1320.                        (p1 + p2) / 2, midy - TCMIDTICHEIGHT - 1,
  1321.                        UIBLACK);
  1322.                     DrawUILine((p1 + p2) / 2 + 1, midy + TCMIDTICHEIGHT,
  1323.                        (p1 + p2) / 2 + 1, midy - TCMIDTICHEIGHT - 1,
  1324.                        UIBLACK);
  1325.                     setlinestyle(DASHEDLINE);
  1326.                 }
  1327.                 DrawUILine(p1, midy,
  1328.                            p2, midy,
  1329.                            UIBLACK);
  1330.                 DrawUILine(p1, midy - 1,
  1331.                            p2, midy - 1,
  1332.                            UIBLACK);
  1333.                 if (!interpolateP)
  1334.                 {
  1335.                     setlinestyle(SOLIDLINE);
  1336.                 }
  1337.                 p1 = p2;
  1338.                 }
  1339.                 for (k = ts1; k <= ts2; ++k)
  1340.                 {
  1341.                 pixel = TimeToPixel(object, timeElements[k]);
  1342.                 SetUIColor(TCSAMPLECOLOR);
  1343.                 FillRealQuad((real) pixel, (real) midy + TCSAMPLESIZE / 2,
  1344.                     (real) pixel - TCSAMPLESIZE / 2, (real) midy,
  1345.                     (real) pixel, (real) midy - TCSAMPLESIZE / 2,
  1346.                     (real) pixel + TCSAMPLESIZE / 2, (real) midy);
  1347.                 }
  1348.             }
  1349.             }
  1350.         }
  1351.         else
  1352.         {
  1353.             /*Eternal object*/
  1354.             SetLineWidth(2);
  1355.             DrawUILine(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1, midy,
  1356.                    right - 1, midy, TCSAMPLECOLOR);
  1357.             SetLineWidth(1);
  1358.         }
  1359.         midy -= TCCELLHEIGHT;
  1360.         ++line;
  1361.         }
  1362.         RestoreClipRect();
  1363.     }
  1364.     }
  1365.     FrameUIRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH,
  1366.         right,
  1367.         bottom + TCGAP + BARWIDTH,
  1368.         top, UIBLACK);
  1369.     DrawUILine(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH,
  1370.         top - TCTIMEHEIGHT,
  1371.         right, top - TCTIMEHEIGHT,
  1372.         UIBLACK);
  1373.     DrawUILine(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH,
  1374.         top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP,
  1375.         right,
  1376.         top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP,
  1377.         UIBLACK);
  1378.     DrawUILine(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH,
  1379.         top - TCCURHEIGHT - TCTIMEHEIGHT - 1,
  1380.         right,
  1381.         top - TCCURHEIGHT - TCTIMEHEIGHT - 1,
  1382.         UIBLACK);
  1383.     FillUIRect(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1384.         right - 1,
  1385.         top - TCTIMEHEIGHT - CEDGE,
  1386.         top - TCTIMEHEIGHT,
  1387.         UIBOTTOMEDGE);
  1388.     FillUIRect(    left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1389.         right - 1,
  1390.         top - TCTIMEHEIGHT - TCCURHEIGHT + CEDGE + 1,
  1391.         top - TCTIMEHEIGHT - CEDGE - 1,
  1392.         timeVar ? UIPGREEN : UIGRAY50);
  1393.     FillUIRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1394.         right - 1,
  1395.         top - TCTIMEHEIGHT - TCCURHEIGHT,
  1396.         top - TCTIMEHEIGHT - TCCURHEIGHT + CEDGE,
  1397.         UITOPEDGE);
  1398.     if (timeVar == NULLOBJ)
  1399.     {
  1400.     FillUIGauzeRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1,
  1401.             right - 1,
  1402.             top - TCTIMEHEIGHT - TCCURHEIGHT,
  1403.             top - TCTIMEHEIGHT,
  1404.             UIBACKGROUND);
  1405.     }
  1406.  
  1407.     if (timeVar)
  1408.     {
  1409.     timePix = TimeToPixel(object, value);
  1410.     if (timePix > left + TCDSWIDTH + 2 * TCGAP + BARWIDTH - TCCURWIDTH &&
  1411.         timePix < right + TCCURWIDTH)
  1412.     {
  1413.         /*Draw the time cursor*/
  1414.         SetClipRect(left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1, right - 1, bottom + TCGAP + BARWIDTH + 1, top - 1);
  1415.         DrawVCursor(timePix, top - 1 - TCTIMEHEIGHT + TCTIMEBOFF, bottom + TCGAP + BARWIDTH + 1);
  1416.         DrawRaisedRect(timePix - TCCURWIDTH / 2, timePix + TCCURWIDTH / 2,
  1417.             top - TCTIMEHEIGHT - TCCURHEIGHT + 1 + CEDGE, top - TCTIMEHEIGHT - 1 - CEDGE,
  1418.             GetPredicate(object, HIGHLIGHTED) ? UIHIBACKGROUND : UIBACKGROUND);
  1419.         RestoreClipRect();
  1420.     }
  1421.     }
  1422.  
  1423.     /*Draw "Current time:"*/
  1424.     SetUIColor(UIBLACK);
  1425.     SetUIFont(TCFONT);
  1426.     DrawAString(LEFTALIGN, left, top - TCTIMEHEIGHT + 1 + TCTIMEBOFF, "Current time:");
  1427. #endif
  1428.     return ObjTrue;
  1429. }
  1430.  
  1431. static ObjPtr AutoScrollTimeControl(control)
  1432. ObjPtr control;
  1433. /*Auto scrolls a time control*/
  1434. {
  1435.     ObjPtr var;
  1436.  
  1437.     var = GetVar(control, VALUE);
  1438.     if (var)
  1439.     {
  1440.     int left, right, bottom, top, timePix;
  1441.     ObjPtr slider;
  1442.     real value, testVal, midVal, lo, hi;
  1443.  
  1444.     value = GetReal(var);
  1445.  
  1446.      Get2DIntBounds(control, &left, &right, &bottom, &top);
  1447.  
  1448.     slider = GetObjectVar("AutoScrollTimeControl", control, HSCROLL);
  1449.     if (!slider)
  1450.     {
  1451.         return ObjFalse;
  1452.     }
  1453.     
  1454.     GetSliderRange(slider, &lo, &hi);
  1455.  
  1456.     testVal = PixelToTime(control, left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1 + TCSCROLLBORDER);
  1457.     if (value < testVal)
  1458.     {
  1459.         /*It's off to the left*/
  1460.         midVal = PixelToTime(control, (left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + right) / 2);
  1461.         SetSliderValue(slider, MAX(lo, value + midVal - testVal));
  1462.         return ObjTrue;
  1463.     }
  1464.  
  1465.     testVal = PixelToTime(control, right - 1 - TCSCROLLBORDER);
  1466.     if (value > testVal)
  1467.     {
  1468.         /*It's off to the right*/
  1469.         midVal = PixelToTime(control, (left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + right) / 2);
  1470.         SetSliderValue(slider, MIN(hi, value + midVal - testVal));
  1471.         return ObjTrue;
  1472.     }
  1473.  
  1474.     return ObjFalse;
  1475.     }
  1476.     else
  1477.     {
  1478.     return ObjFalse;
  1479.     }
  1480. }
  1481.  
  1482. static ObjPtr FindObjectTimeControl(object, name)
  1483. ObjPtr object;
  1484. char *name;
  1485. /*Searches a time control for an object with name*/
  1486. {
  1487.     ObjPtr retVal = NULLOBJ;
  1488.     ObjPtr objName;
  1489.     ObjPtr scrollbar, textBox;
  1490.  
  1491.     /*First check to see if I am the object*/
  1492.     objName = GetVar(object, NAME);
  1493.     if (objName && IsString(objName) && ObjectNameMatches(name, GetString(objName)))
  1494.     {
  1495.     if (!retVal)
  1496.     {
  1497.         retVal = NewList();
  1498.     }
  1499.     PostfixList(retVal, object);
  1500.     }
  1501.  
  1502.     /*Now check the scroll bars*/
  1503.     scrollbar = GetVar(object, HSCROLL);
  1504.     if (scrollbar)
  1505.     {
  1506.     objName = GetVar(scrollbar, NAME);
  1507.     if (objName && IsString(objName) && 0 == strcmp2(GetString(objName), name))
  1508.     {
  1509.         if (!retVal)
  1510.         {
  1511.         retVal = NewList();
  1512.         }
  1513.         PostfixList(retVal, scrollbar);
  1514.     }
  1515.     }
  1516.     scrollbar = GetVar(object, VSCROLL);
  1517.     if (scrollbar)
  1518.     {
  1519.     objName = GetVar(scrollbar, NAME);
  1520.     if (objName && IsString(objName) && 0 == strcmp2(GetString(objName), name))
  1521.     {
  1522.         if (!retVal)
  1523.         {
  1524.         retVal = NewList();
  1525.         }
  1526.         PostfixList(retVal, scrollbar);
  1527.     }
  1528.     }
  1529.  
  1530.     textBox = GetVar(object, READOUT);
  1531.     if (textBox)
  1532.     {
  1533.     objName = GetVar(textBox, NAME);
  1534.     if (objName && IsString(objName) && 0 == strcmp2(GetString(objName), name))
  1535.     {
  1536.         if (!retVal)
  1537.         {
  1538.         retVal = NewList();
  1539.         }
  1540.         PostfixList(retVal, textBox);
  1541.     }
  1542.     }
  1543.     return retVal;
  1544. }
  1545.  
  1546. static ObjPtr KeyDownTimeControl(object, key, flags)
  1547. ObjPtr object;
  1548. int key;
  1549. long flags;
  1550. /*Does a keydown in a time control*/
  1551. {
  1552.     ObjPtr readout;
  1553.     if (key == 0)
  1554.     {
  1555.     /*Do nothing with timeout*/
  1556.     return ObjTrue;
  1557.     }
  1558.  
  1559.     if (AmICurrent(object) &&
  1560.     (key == FK_LEFT_ARROW || key == FK_RIGHT_ARROW ||
  1561.      key == FK_UP_ARROW || key == FK_DOWN_ARROW))
  1562.     {
  1563.     /*It's a keypress here*/
  1564.     ObjPtr var;
  1565.     ObjPtr timeSteps;
  1566.     long index;
  1567.  
  1568.     MakeVar(object, TIMESTEPS);
  1569.     timeSteps = GetVar(object, TIMESTEPS);
  1570.     if (!timeSteps)
  1571.     {
  1572.         return ObjTrue;
  1573.     }
  1574.  
  1575.     var = GetRealVar("KeyDownTimeControl", object, VALUE);
  1576.     if (!var)
  1577.     {
  1578.         return ObjTrue;
  1579.     }
  1580.     
  1581.     index = SearchReal(timeSteps, GetReal(var));
  1582.  
  1583.     if (key == FK_LEFT_ARROW || key == FK_DOWN_ARROW) index -= 2;
  1584.     if (index < 0) index = DIMS(timeSteps)[0] - 1;
  1585.     if (index >= DIMS(timeSteps)[0]) index = 0;
  1586.  
  1587. #ifdef INTERACTIVE
  1588.     DrawInteractive(false);
  1589. #endif
  1590.     SetVar(object, VALUE, NewReal(((real *) ELEMENTS(timeSteps))[index]));
  1591.     UpdateTimeReadout(object);
  1592.     if (logging)
  1593.     {
  1594.         LogControl(object);
  1595.     }
  1596.     AutoScroll(object);
  1597.     ImInvalid(object);
  1598.     ChangedValue(object);
  1599.     }
  1600.  
  1601.     readout = GetVar(object, READOUT);
  1602.     if (readout || AmICurrent(readout))
  1603.     {
  1604.     return KeyDownObject(readout, key, flags);
  1605.     }
  1606.     else
  1607.     {
  1608.     return ObjFalse;
  1609.     }
  1610. }
  1611.  
  1612. #ifdef INTERACTIVE
  1613. static ObjPtr PressTimeControl(object, x, y, flags)
  1614. ObjPtr object;
  1615. int x, y;
  1616. long flags;
  1617. /*Does a press in a time control beginning at x and y.  Returns
  1618.   true iff the press really was in the field.*/
  1619. {
  1620.     int left, right, bottom, top;
  1621.     ObjPtr scrollbar, textBox;
  1622.  
  1623.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  1624.  
  1625.     /*See if it's a press in a scrollbar*/
  1626.     scrollbar = GetVar(object, HSCROLL);
  1627.     if (scrollbar)
  1628.     {
  1629.     if (IsTrue(PressObject(scrollbar, x, y, flags)))
  1630.     {
  1631.         return ObjTrue;
  1632.     }
  1633.     }
  1634.     scrollbar = GetVar(object, VSCROLL);
  1635.     if (scrollbar)
  1636.     {
  1637.     if (IsTrue(PressObject(scrollbar, x, y, flags)))
  1638.     {
  1639.         return ObjTrue;
  1640.     }
  1641.     }
  1642.  
  1643.     /*Now the readout*/
  1644.     textBox = GetVar(object, READOUT);
  1645.     if (textBox)
  1646.     {
  1647.     if (IsTrue(PressObject(textBox, x, y, flags)))
  1648.     {
  1649.         return ObjTrue;
  1650.     }
  1651.     }
  1652.  
  1653.     MakeMeCurrent(object);
  1654.  
  1655.     if (x >= left && x <= right && y >= bottom && y <= top)
  1656.     {
  1657.     /*Hey!  It really was a click in the time control*/
  1658.  
  1659.     if (TOOL(flags) == T_HELP)
  1660.     {
  1661.         ContextHelp(object);
  1662.         return ObjTrue;
  1663.     }
  1664.  
  1665.     if(GetVar(object, VALUE) &&
  1666.        x >= left + TCDSWIDTH + 2 * TCGAP + BARWIDTH &&
  1667.        x <= right &&
  1668.        y >= top - TCTIMEHEIGHT - TCCURHEIGHT &&
  1669.        y <= top - TCTIMEHEIGHT)
  1670.     {
  1671.         /*It's a click in the current time control*/
  1672.         ObjPtr var;
  1673.         real curTime, oldTime;
  1674.         int timePixel;
  1675.         int xOffset;
  1676.         Bool inp;            /*True iff in*/
  1677.         int newX, newY;        /*New x and y*/
  1678.         ObjPtr timeSteps;
  1679.  
  1680.         SaveForUndo(object);
  1681.  
  1682.         /*If constrained, get the time steps*/
  1683.         timeSteps = NULLOBJ;
  1684.         if (flags & F_SHIFTDOWN)
  1685.         {
  1686.         MakeVar(object, TIMESTEPS);
  1687.         timeSteps = GetVar(object, TIMESTEPS);
  1688.         }
  1689.  
  1690.         var = GetVar(object, VALUE);
  1691.         if (var)
  1692.         {
  1693.         curTime = GetReal(var);
  1694.         }
  1695.         else
  1696.         {
  1697.         curTime = 0.0;
  1698.         }
  1699.         timePixel = TimeToPixel(object, curTime);
  1700.  
  1701.         oldTime = curTime;
  1702.  
  1703.         if (x >= timePixel - TCCURWIDTH / 2 && x <= timePixel + TCCURWIDTH / 2)
  1704.         {
  1705.         /*It's a click on the thumb.  Set offset*/
  1706.         xOffset = timePixel - x;
  1707.         }
  1708.         else
  1709.         {
  1710.         /*No offset*/
  1711.         xOffset = 0;
  1712.         }
  1713.  
  1714.         /*Start off inside*/
  1715.         inp = true;
  1716.         SetVar(object, HIGHLIGHTED, ObjTrue);
  1717.  
  1718.         /*Make current x and y bogus*/
  1719.         x = -12345; y = -12345;
  1720.         while (Mouse(&newX, &newY))
  1721.         {
  1722.         if (newX != x)
  1723.         {
  1724.             x = newX;
  1725.             y = newY;
  1726.  
  1727.             /*Check to see if it's outside*/
  1728.             if (y < top - TCTIMEHEIGHT - TCCURHEIGHT - SLOP ||
  1729.                y > top - TCTIMEHEIGHT + SLOP)
  1730.             {
  1731.             /*Yes, it's outside*/
  1732.             if (inp)
  1733.             {
  1734.                 /*Transition from in to out*/
  1735.                 SetVar(object, VALUE, NewReal(oldTime));
  1736.                 UpdateTimeReadout(object);
  1737.                 SetVar(object, HIGHLIGHTED, ObjFalse);
  1738.                 inp = false;
  1739. #ifdef SCROLLTIME
  1740.                 if (AutoScroll(object))
  1741.                 {
  1742.                 x = -12345;
  1743.                 }
  1744. #endif                
  1745.                 DrawMe(object);
  1746.             }
  1747.             }
  1748.             else
  1749.             {
  1750.             /*No, it's inside*/
  1751.             if (!inp)
  1752.             {
  1753.                 /*Transition from out to in*/
  1754.                 inp = true;
  1755.                 SetVar(object, HIGHLIGHTED, ObjTrue);
  1756.             }
  1757.             curTime = PixelToTime(object, x + xOffset);
  1758.             if (timeSteps)
  1759.             {
  1760.                 /*Constrain the time to the closest time step*/
  1761.                 long index;            /*Index into timesteps*/
  1762.                 real *timeElements;        /*Elements of the timesteps*/
  1763.  
  1764.                 timeElements = ELEMENTS(timeSteps);
  1765.  
  1766.                 index = SearchReal(timeSteps, curTime);
  1767.                 if (index <= 0)
  1768.                 {
  1769.                 curTime = timeElements[0];
  1770.                 }
  1771.                 else if (index >= DIMS(timeSteps)[0])
  1772.                 {
  1773.                 curTime = timeElements[DIMS(timeSteps)[0] - 1];
  1774.                 }
  1775.                 else if (timeElements[index] - curTime >
  1776.                      curTime - timeElements[index - 1])
  1777.                 {
  1778.                 curTime = timeElements[index - 1];
  1779.                 }
  1780.                 else
  1781.                 {
  1782.                 curTime = timeElements[index];
  1783.                 }
  1784.             }
  1785.             SetVar(object, VALUE, NewReal(curTime));
  1786.             UpdateTimeReadout(object);
  1787. #ifdef SCROLLTIME
  1788.             if (AutoScroll(object))
  1789.             {
  1790.                 x = -12345;
  1791.             }
  1792. #endif                
  1793.             DrawMe(object);
  1794.             }
  1795.         }
  1796.         }
  1797.  
  1798.         if (inp)
  1799.         {
  1800.         SetVar(object, HIGHLIGHTED, ObjFalse);
  1801.         if (logging)
  1802.         {
  1803.             LogControl(object);
  1804.         }
  1805.         ChangedValue(object);
  1806.         }
  1807.     }
  1808.  
  1809. #if 0
  1810.     int xOff, yOff;
  1811.     GETSCROLL(object, xOff, yOff);
  1812.  
  1813.     SetClipRect(left + FIELDDEPTH, right - FIELDDEPTH, bottom + FIELDDEPTH, top - FIELDDEPTH);
  1814.     SetOrigin(left + FIELDDEPTH + xOff, bottom + FIELDDEPTH + yOff);
  1815.         x -= left + FIELDDEPTH + xOff;
  1816.         y -= bottom + FIELDDEPTH + yOff;
  1817.     
  1818.     pressContents = GetMethod(object, PRESSCONTENTS);
  1819.     if (pressContents)
  1820.     {
  1821.         (*pressContents)(object, x, y, flags);
  1822.     }
  1823.  
  1824.     RestoreOrigin();
  1825.     RestoreClipRect();
  1826. #endif
  1827.     return ObjTrue;
  1828.     }
  1829.     else
  1830.     {
  1831.     return ObjFalse;
  1832.     }
  1833. }
  1834. #endif
  1835.  
  1836. static ObjPtr ChangeTimeControlScroll(scrollbar)
  1837. ObjPtr scrollbar;
  1838. /*Changes the time control scrollbar*/
  1839. {
  1840.     ObjPtr repObj;
  1841.     repObj = GetVar(scrollbar, REPOBJ);
  1842.     if (repObj)
  1843.     {
  1844.     ImInvalid(repObj);
  1845.     }
  1846.     return ObjTrue;
  1847. }
  1848.  
  1849. static ObjPtr MakeTimeControlDatasets(control)
  1850. ObjPtr control;
  1851. /*Makes the time control's DATASETS*/
  1852. {
  1853.     SetVar(control, DATASETS, GetVar(GetVar(control, REPOBJ), DATASETS));
  1854.     RecalcScroll(control);
  1855.     return ObjTrue;
  1856. }
  1857.  
  1858. static ObjPtr MakeTimeControlTimesteps(control)
  1859. ObjPtr control;
  1860. /*Makes the time control's timesteps*/
  1861. {
  1862.     ObjPtr datasets;
  1863.     ObjPtr element;
  1864.     ObjPtr steps = NULLOBJ;
  1865.     long k;
  1866.  
  1867.     MakeVar(control, DATASETS);
  1868.     datasets = GetVar(control, DATASETS);
  1869.     if (datasets)
  1870.     {
  1871.  
  1872.     for (k = 0; k < DIMS(datasets)[0]; ++k)
  1873.     {
  1874.     ObjPtr curTimeSteps;
  1875.     element = GetObjectElement(datasets, &k);
  1876.     MakeVar(element, TIMESTEPS);
  1877.     curTimeSteps = GetVar(element, TIMESTEPS);
  1878.     if (curTimeSteps)
  1879.     {
  1880.         if (steps)
  1881.         {
  1882.         steps = MergeRealArrays(steps, curTimeSteps);
  1883.         }
  1884.         else
  1885.         {
  1886.         steps = curTimeSteps;
  1887.         }
  1888.     }
  1889.     }
  1890.     }
  1891.     SetVar(control, TIMESTEPS, steps);
  1892.     return ObjTrue;
  1893. }
  1894.  
  1895. static ObjPtr MakeTimeControlFormat(control)
  1896. ObjPtr control;
  1897. /*Makes the time control's TIMEFORMAT*/
  1898. {
  1899.     ObjPtr repObj;
  1900.  
  1901.     repObj = GetObjectVar("MakeTimeControlFormat", control, REPOBJ);
  1902.     if (repObj)
  1903.     {
  1904.     MakeVar(repObj, TIMEFORMAT);
  1905.     SetVar(control, TIMEFORMAT, GetVar(repObj, TIMEFORMAT));
  1906.     RecalcScroll(control);
  1907.     }
  1908. }
  1909.  
  1910. static ObjPtr RecalcTimeControlScroll(control)
  1911. ObjPtr control;
  1912. /*Recalcs the time control scrolling and other parameters*/
  1913. {
  1914.     real value;
  1915.     ObjPtr var, scrollbar, repObj, readout;
  1916.     int left, right, bottom, top;
  1917.     ObjPtr datasets;
  1918.     int format;
  1919.  
  1920.     Get2DIntBounds(control, &left, &right, &bottom, &top);
  1921.  
  1922.     repObj = GetVar(control, REPOBJ);
  1923.     MakeVar(control, DATASETS);
  1924.     datasets = GetVar(control, DATASETS);
  1925.     MakeVar(control, TIMEFORMAT);
  1926.     var = GetVar(control, TIMEFORMAT);
  1927.     if (var)
  1928.     {
  1929.     format = GetInt(var);
  1930.     }
  1931.     else
  1932.     {
  1933.     format = TF_SECONDS + TF_SUBSECONDS;
  1934.     }
  1935.  
  1936.     readout = GetObjectVar("RecalcTimeControlScroll", control, READOUT);
  1937.     if (!readout)
  1938.     {
  1939.     return false;
  1940.     }
  1941.  
  1942.     /*Set the horizontal scroll bar*/
  1943.     scrollbar = GetVar(control, HSCROLL);
  1944.     if (scrollbar)
  1945.     {
  1946.     ObjPtr timeSteps;
  1947.     real lo, hi, minTimeStep;
  1948.     Bool stepSet = false;
  1949.     Bool loHiSet = false;
  1950.     Bool someTime = false;
  1951.  
  1952.     /*Calculate timeSteps*/
  1953.     if (datasets)
  1954.     {
  1955.         ObjPtr element;
  1956.         long k;
  1957.  
  1958.         for (k = 0; k < DIMS(datasets)[0]; ++k)
  1959.         {
  1960.         ObjPtr timeSteps;
  1961.  
  1962.         element = GetObjectElement(datasets, &k);
  1963.         MakeVar(element, TIMESTEPS);
  1964.  
  1965.         timeSteps = GetVar(element, TIMESTEPS);
  1966.         if (timeSteps)
  1967.         {
  1968.             ObjPtr deltas;
  1969.             deltas = SortArray(Uniq(RealArrayDeltas(timeSteps)));
  1970.  
  1971.             someTime = true;
  1972.  
  1973.             /*Set the lo and hi*/
  1974.             if (loHiSet)
  1975.             {
  1976.             lo = MIN(lo,
  1977.                  *((real *) ELEMENTS(timeSteps)));
  1978.             hi = MAX(hi,
  1979.                  ((real *) ELEMENTS(timeSteps))[DIMS(timeSteps)[0] - 1]); 
  1980.             }
  1981.             else
  1982.             {
  1983.             lo = *((real *) ELEMENTS(timeSteps));
  1984.             hi = ((real *) ELEMENTS(timeSteps))[DIMS(timeSteps)[0] - 1];
  1985.             loHiSet = true;
  1986.             }
  1987.  
  1988.             /*Set the step only if there is one*/
  1989.             if (DIMS(timeSteps)[0] > 1)
  1990.             {
  1991.             if (stepSet)
  1992.             {
  1993.                 minTimeStep = MIN(minTimeStep,
  1994.                       *((real *) ELEMENTS(deltas)));
  1995.             }
  1996.             else
  1997.             {
  1998.                 minTimeStep = *((real *) ELEMENTS(deltas));
  1999.                 stepSet = true;
  2000.             }
  2001.             }
  2002.         }
  2003.         }
  2004.     }
  2005.     if (someTime)
  2006.     {
  2007.         ActivateTextBox(readout, true);
  2008.     }
  2009.     else
  2010.     {
  2011.         ActivateTextBox(readout, false);
  2012.         /*Kludge to make time go away*/
  2013.         SetVar(control, VALUE, NULLOBJ);
  2014.         SetVar(control, VALUESET, ObjFalse);
  2015.         SetVar(repObj, TIME, NULLOBJ);
  2016.         UpdateTimeReadout(control);
  2017.     }
  2018.     if (loHiSet)
  2019.     {
  2020.         /*Deal with existing value*/
  2021.         var = GetVar(control, VALUE);
  2022.         if (var)
  2023.         {
  2024.         if (!GetPredicate(control, VALUESET))
  2025.         {
  2026.             /*It hasn't been set yet*/
  2027.             SetVar(control, VALUESET, ObjTrue);
  2028.             SetSliderValue(scrollbar, GetReal(var));
  2029.         }
  2030. #if 0
  2031.         /*There already is a value.  Extend lo and hi*/
  2032.         value = GetReal(var);
  2033.         lo = MIN(lo, value);
  2034.         hi = MAX(hi, value);
  2035. #endif
  2036.         }
  2037.         else
  2038.         {
  2039.         /*There's a new value*/
  2040.         value = lo;
  2041.         SetValue(control, NewReal(value));
  2042.         SetSliderValue(scrollbar, value);
  2043.         }
  2044.         SetSliderRange(scrollbar, hi, lo, minTimeStep);
  2045.     }
  2046.     else
  2047.     {
  2048.         SetSliderValue(scrollbar, 0.0);
  2049.         SetSliderRange(scrollbar, 0.0, 1.0, 20.0);
  2050.     }
  2051.     if (stepSet)
  2052.     {
  2053.         double deltaLog;
  2054.         int nTics;
  2055.         real timePerPixel, timeShown, trialDisplay;
  2056.         /*There is more than one time sample.  Calculate time per pixel
  2057.           and scrollbar coverage*/
  2058.         timePerPixel = minTimeStep / TCSTEPPIXELS;
  2059.         SetVar(control, TIMEPERPIXEL, NewReal(timePerPixel));
  2060.         timeShown = (right - 1 - (left + TCDSWIDTH + 2 * TCGAP + BARWIDTH + 1))
  2061.             * timePerPixel;
  2062.         SetPortionShown(scrollbar, timeShown);
  2063.  
  2064.         /*Also calculate DISPLAYSTEP*/
  2065.         if ((format & TF_HOURS) || (format & TF_MINUTES))
  2066.         {
  2067.         /*It's HMS*/
  2068.         if (minTimeStep < 10.0)
  2069.         {
  2070.             /*Might as well ignore hours and minutes*/
  2071.             deltaLog = floor(log((double) minTimeStep) / LOGE10);
  2072.             trialDisplay = pow((double) 10.0, deltaLog);
  2073.             nTics = 10;
  2074.         }
  2075.         else
  2076.         {
  2077.             deltaLog = floor(log((double) minTimeStep) / LOGE10);
  2078.             trialDisplay = pow((double) 60.0, deltaLog);
  2079.             nTics = 10;
  2080.  
  2081.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2082.             {
  2083.             /*Try 5*/
  2084.             trialDisplay *= 5.0;
  2085.             nTics = 5;
  2086.             }
  2087.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2088.             {
  2089.             /*Try 15*/
  2090.             trialDisplay *= 3.0;
  2091.             nTics = 3;
  2092.             }
  2093.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2094.             {
  2095.             /*Try 30*/
  2096.             trialDisplay *= 2.0;
  2097.             nTics = 2;
  2098.             }
  2099.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2100.             {
  2101.             /*Try 60*/
  2102.             trialDisplay *= 2.0;
  2103.             nTics = 4;
  2104.             }
  2105.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2106.             {
  2107.             /*Try 5*/
  2108.             trialDisplay *= 5.0;
  2109.             nTics = 5;
  2110.             }
  2111.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2112.             {
  2113.             /*Try 15*/
  2114.             trialDisplay *= 3.0;
  2115.             nTics = 3;
  2116.             }
  2117.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2118.             {
  2119.             /*Try 30*/
  2120.             trialDisplay *= 2.0;
  2121.             nTics = 2;
  2122.             }
  2123.             if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2124.             {
  2125.             /*Try 60*/
  2126.             trialDisplay *= 2.0;
  2127.             nTics = 4;
  2128.             }
  2129.         }
  2130.         }
  2131.         else
  2132.         {
  2133.         deltaLog = floor(log((double) minTimeStep) / LOGE10);
  2134.         trialDisplay = pow((double) 10.0, deltaLog);
  2135.         nTics = 10;
  2136.         }
  2137.  
  2138.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2139.         {
  2140.         /*Try 2*/
  2141.         trialDisplay *= 2.0;
  2142.         nTics = 2;
  2143.         }
  2144.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2145.         {
  2146.         /*Try 5*/
  2147.         trialDisplay *= 2.5;
  2148.         nTics = 5;
  2149.         }
  2150.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2151.         {
  2152.         /*Try 10*/
  2153.         trialDisplay *= 2.0;
  2154.         nTics = 10;
  2155.         }
  2156.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2157.         {
  2158.         /*Try 2*/
  2159.         trialDisplay *= 2.0;
  2160.         nTics = 2;
  2161.         }
  2162.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2163.         {
  2164.         /*Try 5*/
  2165.         trialDisplay *= 2.5;
  2166.         nTics = 5;
  2167.         }
  2168.         if (trialDisplay / timePerPixel < TCTIMEPIXELS)
  2169.         {
  2170.         /*Try 10*/
  2171.         trialDisplay *= 2.0;
  2172.         nTics = 10;
  2173.         }
  2174.         SetVar(control, DISPLAYSTEP, NewReal(trialDisplay));
  2175.         SetVar(control, DISPLAYTICS, NewInt(nTics));
  2176.     }
  2177.     else
  2178.     {
  2179.         /*No more than one time sample, no need to have any time per pixel*/
  2180.         SetVar(control, TIMEPERPIXEL, NULLOBJ);
  2181.         SetPortionShown(scrollbar, 1.0);
  2182.     }
  2183.     }
  2184.  
  2185.     /*Set the vertical scroll bar*/
  2186.     scrollbar = GetVar(control, VSCROLL);
  2187.     if (scrollbar)
  2188.     {
  2189.     if (datasets)
  2190.     {
  2191.         long dsSize, boxSize, height;
  2192.         dsSize = TCCELLHEIGHT * DIMS(datasets)[0];
  2193.         boxSize = top - TCCURHEIGHT - TCTIMEHEIGHT - TCGAP - 1 - (bottom + TCGAP + BARWIDTH + 1);
  2194.         if (dsSize < boxSize)
  2195.         {
  2196.         height = 0;
  2197.         dsSize = 1;
  2198.         }
  2199.         else
  2200.         {
  2201.         height = dsSize - boxSize;
  2202.         }
  2203.         SetSliderRange(scrollbar, 0.0, (real) -height, TCCELLHEIGHT);
  2204.         SetPortionShown(scrollbar, (real) dsSize);
  2205.     }
  2206.     else
  2207.     {
  2208.         SetSliderRange(scrollbar, 0.0, -1.0, 20.0);
  2209.         SetPortionShown(scrollbar, 1.0);
  2210.     }
  2211.     }
  2212.  
  2213.     return ObjTrue;
  2214. }
  2215.  
  2216. static ObjPtr SetTimeControlValue(control, value)
  2217. ObjPtr control;
  2218. ObjPtr value;
  2219. /*Sets the value of a time control*/
  2220. {
  2221.     if (IsReal(value))
  2222.     {
  2223.     SetVar(control, VALUE, value);
  2224.     }
  2225.     else if (IsInt(value))
  2226.     {
  2227.     SetVar(control, VALUE, NewReal(GetInt(value)));
  2228.     }
  2229.     else
  2230.     {
  2231.     return ObjFalse;
  2232.     }
  2233.     UpdateTimeReadout(control);
  2234.     if (logging)
  2235.     {
  2236.     LogControl(control);
  2237.     }
  2238.     ImInvalid(control);
  2239.     ChangedValue(control);
  2240.     return ObjTrue;
  2241. }
  2242.  
  2243. static ObjPtr ChangeTimeReadout(readout)
  2244. ObjPtr readout;
  2245. /*CHANGEDVALUE for a time control readout*/
  2246. {
  2247.     ObjPtr value;
  2248.     char *s;
  2249.     real t;
  2250.     int f;
  2251.     int position;
  2252.     ObjPtr parent;
  2253.  
  2254.     value = GetValue(readout);
  2255.     if (!value)
  2256.     {
  2257.     return ObjFalse;
  2258.     }
  2259.     s = GetString(value);
  2260.  
  2261.     parent = GetVar(readout, PARENT);
  2262.     if (!parent)
  2263.     {
  2264.     return ObjFalse;
  2265.     }
  2266.     
  2267.     position = ParseTime(&t, &f, s);
  2268.     if (position > 0)
  2269.     {
  2270.     FuncTyp method;
  2271.     /*It's OK*/
  2272.     method = GetMethod(readout, CHANGEDVALUE);
  2273.     SetMethod(readout, CHANGEDVALUE, 0);
  2274.     InhibitLogging(true);
  2275.     SetValue(parent, NewReal(t));
  2276.     AutoScroll(parent);
  2277.     InhibitLogging(false);
  2278.     SetMethod(readout, CHANGEDVALUE, method);
  2279.     }
  2280.     else
  2281.     {
  2282.     WarnUser(CW_NUMBERERROR);
  2283.     return ObjFalse;
  2284.     }
  2285.     return ObjTrue;
  2286. }
  2287.  
  2288. ObjPtr TimeControlBoundsInvalid(object, changeCount)
  2289. ObjPtr object;
  2290. unsigned long changeCount;
  2291. /*For a time control, tests to see if it needs drawing.  Returns
  2292.   NULLOBJ    if it does not
  2293.   array[4]    giving bounds if it does
  2294.   ObjTrue    it it needs to be redrawn but does not know where
  2295. */
  2296. {
  2297.     ObjPtr datasets;
  2298.     ObjPtr myBounds;
  2299.     real boundsElements[4];
  2300.     real testElements[4];
  2301.     ObjPtr test;
  2302.     real myBoundsElements[4];
  2303.     ObjPtr scrollBar, readout;
  2304.     Bool firstTime = true;
  2305.     Bool doubleNoBounds = false;
  2306.  
  2307.     MakeVar(object, APPEARANCE);
  2308.  
  2309.     MakeVar(object, CHANGEDBOUNDS);
  2310.     if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount)
  2311.     {
  2312.     /*Object is not good, so return the bounds*/
  2313.  
  2314.     myBounds = GetVar(object, CHANGEDBOUNDS);
  2315.     if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
  2316.         && DIMS(myBounds)[0] == 4)
  2317.     {
  2318.         return myBounds;
  2319.     }
  2320.     else
  2321.     {
  2322.         return ObjTrue;
  2323.     }
  2324.     }
  2325.  
  2326.     MakeVar(object, BOUNDS);
  2327.     myBounds = GetVar(object, BOUNDS);
  2328.     Array2CArray(myBoundsElements, myBounds);
  2329.  
  2330.  
  2331.     scrollBar = GetVar(object, HSCROLL);
  2332.     if (scrollBar);
  2333.     {
  2334.         test = BoundsInvalid(scrollBar, changeCount);
  2335.         if (test)
  2336.         {
  2337.         /*Hey, the scroll bar needs redrawing*/
  2338.         if (IsRealArray(test))
  2339.         {
  2340.             /*It has a bounds*/
  2341.             if (firstTime)
  2342.             {
  2343.             Array2CArray(boundsElements, test);
  2344.             }
  2345.             else
  2346.             {
  2347.             Array2CArray(testElements, test);
  2348.             if (testElements[0] < boundsElements[0])
  2349.                 boundsElements[0] = testElements[0];
  2350.             if (testElements[1] > boundsElements[1])
  2351.                 boundsElements[1] = testElements[1];
  2352.             if (testElements[2] < boundsElements[2])
  2353.                 boundsElements[2] = testElements[2];
  2354.             if (testElements[3] > boundsElements[3])
  2355.                 boundsElements[3] = testElements[3];
  2356.             }
  2357.         }
  2358.         else
  2359.         {
  2360.             /*It doesn't have a bounds*/
  2361.             if (firstTime)
  2362.             {
  2363.             /*Try to use my bounds*/
  2364.             if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
  2365.                 && DIMS(myBounds)[0] == 4)
  2366.             {
  2367.                 Array2CArray(boundsElements, myBounds);
  2368.             }
  2369.             else
  2370.             {
  2371.                 doubleNoBounds = true;
  2372.             }
  2373.             }
  2374.         }
  2375.         firstTime = false;
  2376.         }
  2377.     }
  2378.  
  2379.     scrollBar = GetVar(object, VSCROLL);
  2380.     if (scrollBar);
  2381.     {
  2382.         test = BoundsInvalid(scrollBar, changeCount);
  2383.         if (test)
  2384.         {
  2385.         /*Hey, the scroll bar needs redrawing*/
  2386.         if (IsRealArray(test))
  2387.         {
  2388.             /*It has a bounds*/
  2389.             if (firstTime)
  2390.             {
  2391.             Array2CArray(boundsElements, test);
  2392.             }
  2393.             else
  2394.             {
  2395.             Array2CArray(testElements, test);
  2396.             if (testElements[0] < boundsElements[0])
  2397.                 boundsElements[0] = testElements[0];
  2398.             if (testElements[1] > boundsElements[1])
  2399.                 boundsElements[1] = testElements[1];
  2400.             if (testElements[2] < boundsElements[2])
  2401.                 boundsElements[2] = testElements[2];
  2402.             if (testElements[3] > boundsElements[3])
  2403.                 boundsElements[3] = testElements[3];
  2404.             }
  2405.         }
  2406.         else
  2407.         {
  2408.             /*It doesn't have a bounds*/
  2409.             if (firstTime)
  2410.             {
  2411.             /*Try to use my bounds*/
  2412.             if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
  2413.                 && DIMS(myBounds)[0] == 4)
  2414.             {
  2415.                 Array2CArray(boundsElements, myBounds);
  2416.             }
  2417.             else
  2418.             {
  2419.                 doubleNoBounds = true;
  2420.             }
  2421.             }
  2422.         }
  2423.         firstTime = false;
  2424.         }
  2425.     }
  2426.  
  2427.     readout = GetVar(object, READOUT);
  2428.     if (readout);
  2429.     {
  2430.         test = BoundsInvalid(readout, changeCount);
  2431.         if (test)
  2432.         {
  2433.         /*Hey, the scroll bar needs redrawing*/
  2434.         if (IsRealArray(test))
  2435.         {
  2436.             /*It has a bounds*/
  2437.             if (firstTime)
  2438.             {
  2439.             Array2CArray(boundsElements, test);
  2440.             }
  2441.             else
  2442.             {
  2443.             Array2CArray(testElements, test);
  2444.             if (testElements[0] < boundsElements[0])
  2445.                 boundsElements[0] = testElements[0];
  2446.             if (testElements[1] > boundsElements[1])
  2447.                 boundsElements[1] = testElements[1];
  2448.             if (testElements[2] < boundsElements[2])
  2449.                 boundsElements[2] = testElements[2];
  2450.             if (testElements[3] > boundsElements[3])
  2451.                 boundsElements[3] = testElements[3];
  2452.             }
  2453.         }
  2454.         else
  2455.         {
  2456.             /*It doesn't have a bounds*/
  2457.             if (firstTime)
  2458.             {
  2459.             /*Try to use my bounds*/
  2460.             if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
  2461.                 && DIMS(myBounds)[0] == 4)
  2462.             {
  2463.                 Array2CArray(boundsElements, myBounds);
  2464.             }
  2465.             else
  2466.             {
  2467.                 doubleNoBounds = true;
  2468.             }
  2469.             }
  2470.         }
  2471.         firstTime = false;
  2472.         }
  2473.     }
  2474.  
  2475.     /*See if we need to change the bounds based on changed datasets*/
  2476.     MakeVar(object, DATASETS);
  2477.     datasets = GetVar(object, DATASETS);
  2478.     if (datasets && IsList(datasets))
  2479.     {
  2480.     /*Go through the datasets*/
  2481.      ThingListPtr runner;
  2482.  
  2483.     runner = LISTOF(datasets);
  2484.     while (runner)
  2485.     {
  2486.         if (GetVarChangeCount(runner -> thing, CHANGED) > changeCount)
  2487.         {
  2488.         boundsElements[0] = myBoundsElements[0];
  2489.         boundsElements[1] = myBoundsElements[1];
  2490.         boundsElements[2] = myBoundsElements[2];
  2491.         boundsElements[3] = myBoundsElements[3];
  2492.         firstTime = false;
  2493.         }
  2494.         MakeVar(runner -> thing, INTERPOLATEP);
  2495.         if (GetVarChangeCount(runner -> thing, INTERPOLATEP) > changeCount)
  2496.         {
  2497.         boundsElements[0] = myBoundsElements[0];
  2498.         boundsElements[1] = myBoundsElements[1];
  2499.         boundsElements[2] = myBoundsElements[2];
  2500.         boundsElements[3] = myBoundsElements[3];
  2501.         firstTime = false;
  2502.         }
  2503.         runner = runner -> next;
  2504.     }
  2505.     }
  2506.  
  2507.  
  2508.     /*Now return the bounds*/
  2509.     if (firstTime != true)
  2510.     {
  2511.         if (doubleNoBounds)
  2512.         {
  2513.         return ObjTrue;
  2514.         }
  2515.         else
  2516.         {
  2517.         ObjPtr retVal;
  2518.         retVal = NewRealArray(1, 4L);
  2519.         CArray2Array(retVal, boundsElements);
  2520.         return retVal;
  2521.         }
  2522.     }
  2523.  
  2524.     return NULLOBJ;
  2525. }
  2526.  
  2527. ObjPtr NewTimeControl(l, r, b, t, name)
  2528. int l, r, b, t;
  2529. char *name;
  2530. /*Makes a new time control within l, r, b, t with name name*/
  2531. {
  2532.     ObjPtr retVal, scrollbar, readout;
  2533.     retVal = NewObject(timeControlClass, 0);
  2534.  
  2535.     if (!retVal)
  2536.     {
  2537.     return NULLOBJ;
  2538.     }
  2539.     Set2DIntBounds(retVal, l, r, b, t);
  2540.     SetVar(retVal, NAME, NewString(name));
  2541.     
  2542.     /*Create the scroll bars*/
  2543.     scrollbar = NewScrollbar(l + TCDSWIDTH + TCGAP, l + TCDSWIDTH + TCGAP + BARWIDTH,
  2544.                  b + BARWIDTH + TCGAP,
  2545.                  t - TCTIMEHEIGHT - TCCURHEIGHT - TCGAP,
  2546.                  "Dataset Scroll");
  2547.     SetVar(scrollbar, PARENT, retVal);
  2548.     SetVar(scrollbar, REPOBJ, retVal);
  2549.     SetMethod(scrollbar, CHANGEDVALUE, ChangeTimeControlScroll);
  2550.     SetVar(retVal, VSCROLL, scrollbar);
  2551.  
  2552.     scrollbar = NewScrollbar(l + TCDSWIDTH + 2 * TCGAP + BARWIDTH, r,
  2553.                  b,
  2554.                  b + BARWIDTH,
  2555.                  "Time Scroll");
  2556.     SetVar(scrollbar, PARENT, retVal);
  2557.     SetVar(scrollbar, REPOBJ, retVal);
  2558.     SetMethod(scrollbar, CHANGEDVALUE, ChangeTimeControlScroll);
  2559.     SetVar(retVal, HSCROLL, scrollbar);
  2560.  
  2561.     /*Create the readout*/
  2562.     readout = NewTextBox(l, l + TCDSWIDTH + TCGAP + BARWIDTH,
  2563.     t - TCTIMEHEIGHT - TCCURHEIGHT, t - TCTIMEHEIGHT,
  2564.     EDITABLE + WITH_PIT + ONE_LINE, "Time Readout", "0.0");
  2565.     SetMethod(readout, CHANGEDVALUE, ChangeTimeReadout);
  2566.     SetVar(readout, PARENT, retVal);
  2567.     SetTextAlign(readout, RIGHTALIGN);
  2568.     SetVar(retVal, READOUT, readout);
  2569.  
  2570.     return retVal;
  2571. }
  2572.  
  2573. void InitTimers()
  2574. /*Initializes the timers system*/
  2575. {
  2576.     int k;
  2577.     struct tms buffer;
  2578.  
  2579.     startTime = times(&buffer);
  2580.  
  2581.     timedObjClass = NewObject(NULLOBJ, 0);
  2582.     SetVar(timedObjClass, CLASSID, NewInt(CLASS_TIMEDOBJ));
  2583.     DeclareDependency(timedObjClass, TIMEBOUNDS, TIMESTEPS);    
  2584.     DeclareDependency(timedObjClass, TIMEBOUNDS, TIMEDATA);    
  2585.     SetMethod(timedObjClass, TIMEBOUNDS, MakeTimeBounds);
  2586.     SetMethod(timedObjClass, REGISTERFIELD, RegisterTimedField);
  2587.  
  2588.     AddToReferenceList(timedObjClass);
  2589.  
  2590.     timeControlClass = NewObject(controlClass, 0);
  2591.     SetMethod(timeControlClass, DRAW, DrawTimeControl);
  2592.     SetMethod(timeControlClass, FINDOBJECT, FindObjectTimeControl);
  2593. #ifdef INTERACTIVE
  2594.     SetMethod(timeControlClass, PRESS, PressTimeControl);
  2595. #endif
  2596.     SetMethod(timeControlClass, BOUNDSINVALID, TimeControlBoundsInvalid);
  2597.     SetMethod(timeControlClass, KEYDOWN, KeyDownTimeControl);
  2598.     SetMethod(timeControlClass, RECALCSCROLL, RecalcTimeControlScroll);
  2599.     SetMethod(timeControlClass, SETVAL, SetTimeControlValue);
  2600.     SetMethod(timeControlClass, AUTOSCROLL, AutoScrollTimeControl);
  2601.     SetVar(timeControlClass, TYPESTRING, NewString("time control"));
  2602.     SetVar(timeControlClass, HELPSTRING,
  2603.     NewString("This control shows the current time in a clock.  On the \
  2604. left is a box containing the names all the datasets in all the visualization objects that this \
  2605. clock controls.  On the right is the time line of each dataset, lined up with \
  2606. the dataset name.  A horizontal blue line means that the dataset is eternal and \
  2607. is defined for all time.  Blue diamonds indicate time steps in time-dependent \
  2608. data.  Between the diamonds, dashed black lines indicate that the nearest \
  2609. timestep to the current time will be displayed.  Solid black lines indicate that the data will be \
  2610. interpolated between time steps.  You can change whether a dataset will be \
  2611. interpolated using the Show Info button \
  2612. in the Datasets window.\n\
  2613. \n\
  2614. At the top left is a text box that shows the current time.  At the top right is \
  2615. a slider connected to a cursor that shows the current time as well as its relation \
  2616. to the datasets controlled by the clock.  You can change the current time either by \
  2617. editing the text or moving the indicator.  Hold down the Shift key while moving \
  2618. the indicator to constrain to actual time steps.  If none of the datasets are time-dependent, \
  2619. both of these will show as gray."));
  2620.     DeclareIndirectDependency(timeControlClass, DATASETS, REPOBJ, DATASETS);
  2621.     SetMethod(timeControlClass, DATASETS, MakeTimeControlDatasets);
  2622.     DeclareIndirectDependency(timeControlClass, TIMEFORMAT, REPOBJ, TIMEFORMAT);
  2623.     SetMethod(timeControlClass, TIMEFORMAT, MakeTimeControlFormat);
  2624.     DeclareDependency(timeControlClass, TIMESTEPS, DATASETS);
  2625.     SetMethod(timeControlClass, TIMESTEPS, MakeTimeControlTimesteps);
  2626.     AddToReferenceList(timeControlClass);
  2627.  
  2628.     for (k = 0; k < NALARMS; ++k)
  2629.     {
  2630.     alarms[k] . object = 0;
  2631.     }
  2632. }
  2633.  
  2634. void KillTimers()
  2635. /*Kills the timers system*/
  2636. {
  2637.     int k;
  2638.     
  2639.     DeleteThing(timeControlClass);
  2640.     DeleteThing(timedObjClass);
  2641.     for (k = 0; k < NALARMS; ++k)
  2642.     {
  2643.     if (alarms[k] . object)
  2644.     {
  2645.         ObjPtr object;
  2646.         object = alarms[k] . object;
  2647.         alarms[k] . object = 0;
  2648.         DeleteThing(object);
  2649.     }
  2650.     }
  2651. }
  2652.